home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / vim_src.zip / STORAGE.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  19KB  |  886 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMitation
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * storage.c: allocation of lines and management of them
  13.  *
  14.  * part 1: storage allocation for the lines and blocks of the current file
  15.  * part 2: managing of the pointer blocks
  16.  */
  17.  
  18. #include "vim.h"
  19. #include "globals.h"
  20. #include "proto.h"
  21.  
  22. /***************************************************************************
  23.  * part 1: storage allocation for the lines and blocks of the current file *
  24.  ***************************************************************************/
  25.  
  26. /*
  27.  * Memory is allocated in relatively large blocks. These blocks are linked
  28.  * in the allocated block list, headed by m_ahead. They are all freed
  29.  * when abandoning a file.
  30.  *
  31.  * The available chunks of memory are kept in the free chunk list, headed
  32.  * by m_fhead. This is a circular list. m_search points to the chunk before the
  33.  * chunk that was freed/allocated the last time. alloc_line() gets a chunk
  34.  * from the free list; free_line() returns a chunk to the free list.
  35.  */
  36.  
  37.     /* on the Amiga the blocksize must not be a multiple of 256 */
  38.     /* with MS-Dos the blocksize must be larger than 255 */
  39.     /* For Unix it does not really matter */
  40. #define MEMBLOCKSIZE 2044
  41.  
  42. typedef struct m_info info_t;
  43.  
  44. /*
  45.  * There are two types of in-use memory chunks:
  46.  *    1. those that are allocated by readfile(). These are always preceded
  47.  *        by a NUL character and end in a NUL character. The chunk must not
  48.  *        contain other NUL characters. The preceding NUL is used to
  49.  *        determine the chunk type. The ending NUL is used to determine the
  50.  *        end of the chunk. The preceding NUL is not part of the chunk, the
  51.  *        ending NUL is. 
  52.  *  2. the other chunks have been allocated with alloc_line(). They are
  53.  *        preceded by a non-NUL character. This is used to determine the chunk
  54.  *        type. The non-NUL may be part of a size field or may be an extra 0xff
  55.  *        byte. The chunk always ends in a NUL character and may also contain
  56.  *        a NUL character. The size field contains the size of the chunk,
  57.  *        including the size field. The trailing NUL may be used by a possibly
  58.  *        follwing type 1 chunk. The preceding size, the optional 0xff and the
  59.  *        trailing NUL are all part of the chunk.
  60.  *
  61.  * When the chunk is not in-use it is preceded with the m_info structure.
  62.  * The m_next field links it in the free chunk list. It must still end in
  63.  * a NUL byte, because it may be followed by a type 1 chunk!
  64.  *
  65.  * When in-use we must make sure there is a non-NUL byte in front of type
  66.  * 2 chunks.
  67.  *
  68.  * On the Amiga this means that the size must not be a multiple of 256.
  69.  * This is done by multiplying the size by 2 and adding one.
  70.  *
  71.  * On MS-DOS the size must be larger than 255. This is done by adding 256
  72.  * to the size.
  73.  *
  74.  * On Unix systems an extra 0xff byte is added. This costs 4 bytes because
  75.  * pointers must be kept long-aligned.
  76.  *
  77.  * On most unix systems structures have to be longword aligned.
  78.  * On most other systems they are short (16 bit) aligned.
  79.  */
  80.  
  81. #ifdef UNIX
  82. # define ALIGN_LONG        /* 32 bit alignment and use filler byte */
  83. #else
  84. # ifdef AMIGA
  85. #  define LOWBYTE        /* size must not be multiple of 256 */
  86. # else
  87. #  ifdef MSDOS
  88. #   define HIGHBYTE        /* size must be larger than 255 */
  89. #  else
  90.     you must define something!
  91. #  endif
  92. # endif
  93. #endif
  94.  
  95. struct m_info
  96. {
  97. #ifdef ALIGN_LONG
  98.     u_long     m_size;    /* size of the chunk (including m_info) */
  99. #else
  100.     u_short  m_size;    /* size of the chunk (including m_info) */
  101. #endif
  102.     info_t    *m_next;    /* pointer to next free chunk in the list */
  103. };
  104.  
  105. #ifdef ALIGN_LONG
  106.     /* size of m_size + room for 0xff byte */
  107. # define M_OFFSET (sizeof(u_long) * 2)
  108. #else
  109.     /* size of m_size */
  110. # define M_OFFSET (sizeof(u_short))
  111. #endif
  112.  
  113. static char     *m_ahead = NULL;        /* head of allocated memory block list */
  114.  
  115. static info_t m_fhead = {0, NULL};    /* head of free chunk list */
  116.  
  117. static info_t *m_search = NULL;     /* pointer to chunk before previously
  118.                                        allocated/freed chunk */
  119.  
  120. #ifdef DEBUG
  121. # ifdef AMIGA
  122. m_error()
  123. {
  124.     printf("list error\n");
  125. }
  126. # endif
  127.  
  128. check_list()
  129. {
  130. # ifdef AMIGA
  131.     register info_t *mp;
  132.  
  133.     for (mp = &m_fhead; ; )
  134.     {
  135.         /*
  136.          * adjust these addresses for the actual available memory!
  137.          */
  138.         if (mp >= 0x080000L && mp < 0x200000L ||
  139.             mp >= 0x400000L && mp < 0xc00000 ||
  140.             mp >= 0xc80000 || mp->m_next->m_size > 23000)
  141.         {
  142.             m_error();
  143.             return 1;
  144.         }
  145.         mp = mp->m_next;
  146.         if (mp == &m_fhead)
  147.             break;
  148.     }
  149. # endif
  150.     return 0;
  151. }
  152. #endif    /* DEBUG */
  153.  
  154. /*
  155.  * Allocate a block of memory and link it in the allocated list, so that
  156.  * the block will be freed when abandoning the file.
  157.  */
  158.     char *
  159. m_blockalloc(size, message)
  160.     u_long    size;
  161.     int        message;
  162. {
  163.     char *p;
  164.  
  165.     p = lalloc(size + sizeof(char *), message);
  166.     if (p != NULL)
  167.     {
  168.         *(char **)p = m_ahead;
  169.         m_ahead = p;
  170.         p += sizeof(char *);
  171.     }
  172.     return p;
  173. }
  174.  
  175. /*
  176.  * free all allocated memory blocks
  177.  */
  178.     void
  179. m_blockfree()
  180. {
  181.     char *p, *np;
  182.  
  183.     for (p = m_ahead; p != NULL; p = np)
  184.     {
  185.         np = *(char **)p;
  186.         free(p);
  187.     }
  188.     m_ahead = NULL;
  189.     m_search = NULL;
  190. }
  191.  
  192. /*
  193.  * Free a chunk of memory which was
  194.  *  1. inserted with readfile(); these are preceded with a NUL byte
  195.  *  2. allocated with alloc_line(); these are preceded with a non-NUL byte
  196.  * Insert the chunk into the free list, keeping it sorted on address.
  197.  */
  198.     void
  199. free_line(ptr)
  200.     char *ptr;
  201. {
  202.     info_t            *mp;
  203.     register info_t *next;
  204.     info_t            *prev, *curr;
  205.     long            len;
  206.  
  207. #ifdef DEBUG
  208.     if (check_list())
  209.         return;
  210. #endif
  211.     if (ptr == NULL || ptr == IObuff)
  212.         return;    /* illegal address can happen in out-of-memory situations */
  213.  
  214.     if (*(ptr - 1) == NUL)        /* type 1 chunk: no size field */
  215.     {
  216. #ifdef ALIGN_LONG        /* use longword alignment */
  217.         long c;
  218.  
  219.         len = strlen(ptr) + 1;
  220.         if ((c = ((long)ptr & 3)) != 0)     /* lose some bytes */
  221.         {
  222.             c = 4 - c;
  223.             ptr += c;
  224.             len -= c;
  225.         }
  226. #else            /* use short (16 bit) alignment */
  227.         len = strlen(ptr) + 1;
  228.         if ((long)ptr & 1)                    /* lose a byte */
  229.         {
  230.             ++ptr;
  231.             --len;
  232.         }
  233. #endif    /* ALIGN_LONG */
  234.  
  235.         /* we must be able to store size, pointer and a trailing NUL */
  236.         /* otherwise we can't fit it in the free list */
  237.         if (len <= (long)sizeof(info_t))
  238.             return;
  239.         mp = (info_t *)ptr;
  240.         mp->m_size = len;
  241.     }
  242. #ifdef ALIGN_LONG
  243.     else if ((*(ptr - 1) & 0xff) == 0xff)    /* type 2 chunk: has size field */
  244.     {
  245.         mp = (info_t *)(ptr - M_OFFSET);
  246.     }
  247.     else
  248.     {
  249.         emsg("Illegal chunk");
  250.         return;
  251.     }
  252. #endif
  253. #ifdef LOWBYTE
  254.     else             /* type 2 chunk: has size field */
  255.     {
  256.         mp = (info_t *)(ptr - M_OFFSET);
  257.         mp->m_size >>= 1;
  258.     }
  259. #endif
  260. #ifdef HIGHBYTE
  261.     else             /* type 2 chunk: has size field */
  262.     {
  263.         mp = (info_t *)(ptr - M_OFFSET);
  264.         mp->m_size -= 256;
  265.     }
  266. #endif
  267.  
  268.     curr = NULL;
  269.     /* if mp is smaller than m_search->m_next we start at m_fhead */
  270.     if (mp < (m_search->m_next))
  271.         next = &m_fhead;
  272.     else
  273.         next = m_search;
  274.     do
  275.     {
  276.         prev = curr;
  277.         curr = next;
  278.         next = next->m_next;
  279.     }
  280.     while (mp > next && next != &m_fhead);
  281.  
  282. /* if *mp and *next are concatenated, join them into one chunk */
  283.     if ((char *)mp + mp->m_size == (char *)next)
  284.     {
  285.         mp->m_size += next->m_size;
  286.         mp->m_next = next->m_next;
  287.     }
  288.     else
  289.         mp->m_next = next;
  290.  
  291. /* if *curr and *mp are concatenated, join them */
  292.     if (prev != NULL && (char *)curr + curr->m_size == (char *)mp)
  293.     {
  294.         curr->m_size += mp->m_size;
  295.         curr->m_next = mp->m_next;
  296.         m_search = prev;
  297.     }
  298.     else
  299.     {
  300.         curr->m_next = mp;
  301.         m_search = curr;    /* put m_search before freed chunk */
  302.     }
  303. #ifdef DEBUG
  304.     check_list();
  305. #endif
  306. }
  307.  
  308. /*
  309.  * Allocate and initialize a new line structure with room for at least
  310.  * 'size' characters.
  311.  */
  312.     char *
  313. alloc_line(size)
  314.     register unsigned size;
  315. {
  316.     register info_t *mp, *mprev, *mp2;
  317.     int         size_align;
  318.  
  319. #ifdef DEBUG
  320.     if (m_search != NULL && check_list())
  321.         return NULL;
  322. #endif
  323. /*
  324.  * Add room for size field, optional 0xff byte and trailing NUL byte.
  325.  * Adjust for minimal size (must be able to store info_t
  326.  * plus a trailing NUL, so the chunk can be released again)
  327.  */
  328.     size += M_OFFSET + 1;
  329.     if (size < sizeof(info_t) + 1)
  330.       size = sizeof(info_t) + 1;
  331.  
  332. /*
  333.  * round size up for alignment
  334.  */
  335. #ifdef ALIGN_LONG            /* use longword alignment */
  336.     size_align = (size + 3) & ~3;
  337. #else /* ALIGN_LONG */    /* use short (16 bit) alignment */
  338.     size_align = (size + 1) & ~1;
  339. #endif    /* ALIGN_LONG */
  340.  
  341. /* if m_search is NULL we have to initialize the free list */
  342.     if (m_search == NULL)
  343.     {
  344.         m_search = &m_fhead;
  345.         m_fhead.m_next = &m_fhead;
  346.     }
  347.  
  348. /* search for space in free list */
  349.     mprev = m_search;
  350.     for (mp = m_search->m_next; mp->m_size < size; mp = mp->m_next)
  351.     {
  352.         if (mp == m_search)
  353.         {
  354.             int        n = (size_align > (MEMBLOCKSIZE / 4) ? size_align : MEMBLOCKSIZE);
  355.  
  356.             mp = (info_t *)m_blockalloc((u_long)n, TRUE);
  357.             if (mp == NULL)
  358.                 return (NULL);
  359. #ifdef HIGHBYTE
  360.             mp->m_size = n + 256;
  361. #endif
  362. #ifdef LOWBYTE
  363.             mp->m_size = (n << 1) + 1;
  364. #endif
  365. #ifdef ALIGN_LONG
  366.             mp->m_size = n;
  367.             *((u_char *)mp + M_OFFSET - 1) = 0xff;
  368. #endif
  369.             free_line((char *)mp + M_OFFSET);
  370.             mp = m_search;
  371.         }
  372.         mprev = mp;
  373.     }
  374.  
  375. /* if the chunk we found is large enough, split it up in two */
  376.     if ((long)mp->m_size - size_align >= (long)(sizeof(info_t) + 1))
  377.     {
  378.         mp2 = (info_t *)((char *)mp + size_align);
  379.         mp2->m_size = mp->m_size - size_align;
  380.         mp2->m_next = mp->m_next;
  381.         mprev->m_next = mp2;
  382.         mp->m_size = size_align;
  383.     }
  384.     else                    /* remove *mp from the free list */
  385.     {
  386.         mprev->m_next = mp->m_next;
  387.     }
  388.     m_search = mprev;
  389.  
  390. #ifdef HIGHBYTE
  391.     mp->m_size += 256;
  392. #endif
  393. #ifdef LOWBYTE
  394.     mp->m_size = (mp->m_size << 1) + 1;
  395. #endif
  396.     mp = (info_t *)((char *)mp + M_OFFSET);
  397. #ifdef ALIGN_LONG
  398.     *((u_char *)mp - 1) = 0xff;            /* mark type 2 chunk */
  399. #endif
  400.     *(char *)mp = NUL;                    /* set the first byte to NUL */
  401. #ifdef DEBUG
  402.     check_list();
  403. #endif
  404.  
  405.     return ((char *)mp);
  406. }
  407.  
  408. /*
  409.  * save_line(): allocate memory with alloc_line() and copy the
  410.  * string 'src' into it.
  411.  */
  412.     char *
  413. save_line(src)
  414.     register char *src;
  415. {
  416.     register char *dst;
  417.     register unsigned len;
  418.  
  419.     len = strlen(src);
  420.     if ((dst = alloc_line(len)) != NULL)
  421.         memmove(dst, src, (size_t)(len + 1));
  422.     return (dst);
  423. }
  424.  
  425. /******************************************
  426.  * part 2: managing of the pointer blocks *
  427.  ******************************************/
  428.  
  429. typedef struct block block_t;
  430.  
  431. #ifdef BLOCK_SIZE
  432. # undef BLOCK_SIZE    /* for Linux: is in limits.h */
  433. #endif
  434.  
  435. #define BLOCK_SIZE 40
  436.  
  437. struct block
  438. {
  439.     u_short  b_count;                /* current number of pointers in b_ptr */
  440.     block_t *b_next;                /* pointer to next block */
  441.     block_t *b_prev;                /* pointer to previous block */
  442.     char    *b_ptr[BLOCK_SIZE];        /* pointers to the lines */
  443.     char     b_flags[BLOCK_SIZE];    /* see below */
  444. };
  445.  
  446. #define B_MARKED    0x01        /* mark for :global command */
  447.  
  448. static block_t *first_block;    /* pointer to first block in block list */
  449. static block_t *last_block;        /* pointer to last block in block list */
  450.  
  451. static block_t *curr_block;        /* block used by nr2ptr */
  452. static linenr_t curr_count;        /* first line number of block curr_block */
  453.  
  454. static block_t *alloc_block __ARGS((void));
  455.  
  456.     static block_t *
  457. alloc_block()
  458. {
  459.     block_t *p;
  460.  
  461.     p = (block_t *)(alloc_line((unsigned)sizeof(block_t)));
  462.     if (p != NULL)
  463.     {
  464.         memset((char *)p, 0, sizeof(block_t));
  465.     }
  466.     return (p);
  467. }
  468.  
  469. /*
  470.  * filealloc() - construct an initial empty file buffer
  471.  */
  472.     void
  473. filealloc()
  474. {
  475.     first_block = last_block = alloc_block();
  476.     if (first_block == NULL || (first_block->b_ptr[0] = alloc_line(0)) == NULL)
  477.         getout(1);
  478.     first_block->b_count = 1;
  479.     Curpos.lnum = 1;
  480.     Curswant = Curpos.col = 0;
  481.     Topline = 1;
  482.     Botline = 2;
  483.     line_count = 1;
  484.     curr_count = 0;
  485.     clrallmarks();
  486.     clrtags();
  487. }
  488.  
  489. /*
  490.  * freeall() - free the current buffer
  491.  *
  492.  * Free all lines in the current buffer.
  493.  */
  494.     void
  495. freeall()
  496. {
  497.     m_blockfree();
  498.     line_count = 0;
  499.     s_ins(0, 0, TRUE);    /* invalidate Line arrays */
  500.     u_clearall();
  501. }
  502.  
  503. /*
  504.  * Get the pointer to the line 'nr'.
  505.  * This function is used a lot for sequential access (writeit, search),
  506.  * so that is what it is optimized for.
  507.  */
  508.     char *
  509. nr2ptr(nr)
  510.     register linenr_t nr;
  511. {
  512.     register linenr_t count;
  513.     register block_t *bp = NULL;
  514.  
  515.     if ((count = curr_count) == 0 || nr >= count + (bp = curr_block)->b_count || nr < count)
  516.     {
  517.         if (nr < 1 || nr > line_count)
  518.         {
  519.             emsg("nr2ptr: illegal nr");
  520.             return (IObuff);    /* always return a valid ptr */
  521.         }
  522.  
  523.         /*
  524.          * three ways to find the pointer:
  525.          * 1. first pointer in the next block (fast for sequential access)
  526.          * 2. search forward
  527.          * 3. search backward
  528.          */
  529.         if (count && nr == count + bp->b_count)        /* in next block */
  530.         {
  531.             count = nr;
  532.             bp = bp->b_next;
  533.         }
  534.         else if (nr <= (count + line_count) / 2 ||
  535.                 (nr <= count && nr <= count / 2))
  536.         {
  537.                                                     /* search forward */
  538.             if (nr < count || count == 0)
  539.             {
  540.                 count = 1;
  541.                 bp = first_block;
  542.             }
  543.             while (bp != NULL)
  544.             {
  545.                 count += bp->b_count;
  546.                 if (nr < count)
  547.                 {
  548.                     count -= bp->b_count;
  549.                     break;
  550.                 }
  551.                 bp = bp->b_next;
  552.             }
  553.         }
  554.         else
  555.         {                                            /* search backward */
  556.             if (nr < count)
  557.                 bp = bp->b_prev;
  558.             else
  559.             {
  560.                 bp = last_block;
  561.                 count = line_count + 1;
  562.             }
  563.             while (bp != NULL)
  564.             {
  565.                 count -= bp->b_count;
  566.                 if (nr >= count)
  567.                     break;
  568.                 bp = bp->b_prev;
  569.             }
  570.         }
  571.         
  572.         if (bp == NULL)
  573.         {
  574.             emsg("nr2ptr: strorage corrupt");
  575.             curr_count = 0;
  576.             return (IObuff);
  577.         }
  578.         curr_count = count;
  579.         curr_block = bp;
  580.     }
  581.     return (bp->b_ptr[nr - count]);
  582. }
  583.  
  584. /*
  585.  * pos2ptr: get pointer to position 'pos'
  586.  */
  587.     char *
  588. pos2ptr(pos)
  589.     FPOS    *pos;
  590. {
  591.     return (nr2ptr(pos->lnum) + pos->col);
  592. }
  593.  
  594. /*
  595.  * set the B_MARKED flag for line 'lnum'
  596.  */
  597.     void
  598. setmarked(lnum)
  599.     linenr_t lnum;
  600. {
  601.     nr2ptr(lnum);
  602.     curr_block->b_flags[lnum - curr_count] |= B_MARKED;
  603. }
  604.  
  605. /*
  606.  * find the first line with its B_MARKED flag set
  607.  */
  608.     linenr_t
  609. firstmarked()
  610. {
  611.     register block_t    *bp;
  612.     register linenr_t    lnum;
  613.     register int        i;
  614.  
  615.     for (bp = first_block, lnum = 1; bp != NULL; bp = bp->b_next)
  616.         for (i = 0; i < bp->b_count; ++i, ++lnum)
  617.             if (bp->b_flags[i] & B_MARKED)
  618.             {
  619.                 bp->b_flags[i] &= ~B_MARKED;
  620.                 return lnum;
  621.             }
  622.     return (linenr_t) 0;
  623. }
  624.  
  625. /*
  626.  * clear all B_MARKED flags
  627.  */
  628.     void
  629. clearmarked()
  630. {
  631.     register block_t    *bp;
  632.     register int        i;
  633.  
  634.     for (bp = first_block; bp != NULL; bp = bp->b_next)
  635.         for (i = bp->b_count; --i >= 0; )
  636.                 bp->b_flags[i] &= ~B_MARKED;
  637. }
  638.  
  639. /*
  640.  * a pointer to a line is converted into a line number
  641.  * we start at line number 'start'
  642.  * this is a bit slow, but it is used for marks and undo only
  643.  */
  644.     linenr_t
  645. ptr2nr(ptr, start)
  646.     char *ptr;
  647.     linenr_t start;
  648. {
  649.     block_t *bp;
  650.     register linenr_t nr;
  651.     register char **pp;
  652.     register int    i;
  653.  
  654.     if (ptr == NULL)
  655.         return (linenr_t)0;
  656.  
  657.     if (start == 0)
  658.         start = 1;
  659.     nr2ptr(start);        /* set curr_block and curr_count */
  660.  
  661.     for (nr = curr_count, bp = curr_block; bp != NULL; bp = bp->b_next)
  662.         for (pp = bp->b_ptr, i = bp->b_count; --i >= 0; ++nr)
  663.             if (*pp++ == ptr)
  664.                 return (nr);
  665.     return (linenr_t)0;
  666. }
  667.  
  668. /*
  669.  * appendline: add a line
  670.  *    return TRUE when succesful
  671.  */
  672.     int
  673. appendline(after, s)
  674.     linenr_t    after;
  675.     char        *s;
  676. {
  677.     register block_t    *bp;
  678.     block_t             *nbp;
  679.     linenr_t            count;
  680.     register int        i;
  681.  
  682.     if (s == NULL)        /* don't insert NULL pointers! */
  683.         return FALSE;
  684.     if (after == 0)     /* insert in front of first line */
  685.     {
  686.         bp = first_block;
  687.         count = 1;
  688.         if (bufempty())     /* simply replace dummy line */
  689.         {
  690.             free_line(bp->b_ptr[0]);
  691.             bp->b_ptr[0] = s;
  692.             return TRUE;
  693.         }
  694.         curr_count = 0; /* curr_block will become invalid */
  695.     }
  696.     else
  697.     {
  698.         (void)nr2ptr(after);    /* find block */
  699.         bp = curr_block;
  700.         count = curr_count;
  701.     }
  702.  
  703.     ++line_count;
  704.     i = bp->b_count;
  705.     if (i < BLOCK_SIZE)            /* there is place in the current block */
  706. /* move ptrs one place forward to make space for new one */
  707.     {
  708.         register char **pp;
  709.         register char *fp;
  710.  
  711.         pp = &(bp->b_ptr[i]);
  712.         fp = &(bp->b_flags[i]);
  713.         for (i += count - after - 1; --i >= 0; --pp, --fp)
  714.         {
  715.             *pp = *(pp - 1);
  716.             *fp = *(fp - 1);
  717.         }
  718.         *pp = s;
  719.         *fp = 0;
  720.         ++bp->b_count;
  721.         return TRUE;
  722.     }
  723.  
  724. /* need to allocate a new block */
  725.     nbp = alloc_block();
  726.     if (nbp == NULL)
  727.     {
  728.         --line_count;
  729.         free_line(s);
  730.         return FALSE;
  731.     }
  732.  
  733. /* put new block in linked list */
  734.     if (after == 0) /* put new block in front of linked list */
  735.     {
  736.         bp->b_prev = nbp;
  737.         nbp->b_next = bp;
  738.         first_block = nbp;
  739.         nbp->b_ptr[0] = s;
  740.         nbp->b_count = 1;
  741.         return TRUE;
  742.     }
  743.  
  744.     /* insert new block in linked list after bp */
  745.     nbp->b_next = bp->b_next;
  746.     bp->b_next = nbp;
  747.     nbp->b_prev = bp;
  748.     if (nbp->b_next == NULL)
  749.         last_block = nbp;
  750.     else
  751.         nbp->b_next->b_prev = nbp;
  752.  
  753.     if (after - count + 1 == BLOCK_SIZE) /* put s in new block */
  754.     {
  755.         nbp->b_ptr[0] = s;
  756.         nbp->b_count = 1;
  757.         return TRUE;
  758.     }
  759.  
  760.     /* move some ptrs from full block to new block */
  761.     {
  762.         register int j = 0;
  763.  
  764.         bp->b_count = after - count + 1;    /* number of ptrs remaining */
  765.         i = BLOCK_SIZE - bp->b_count;        /* number of ptrs to be moved */
  766.         nbp->b_count = i;
  767.         while (--i >= 0)
  768.         {
  769.             j = bp->b_count + i;
  770.             nbp->b_ptr[i] = bp->b_ptr[j];
  771.             nbp->b_flags[i] = bp->b_flags[j];
  772.         }
  773.         bp->b_ptr[j] = s;
  774.         bp->b_flags[j] = 0;
  775.         ++bp->b_count;
  776.         return TRUE;
  777.     }
  778. }
  779.  
  780. /*
  781.  * delsline: delete line from storage
  782.  *
  783.  * the line is turned over to the caller
  784.  */
  785.     char *
  786. delsline(nr)
  787.     linenr_t nr;
  788. {
  789.     char    *ptr;
  790.     register block_t    *bp;
  791.     register char **pp;
  792.     register char *fp;
  793.     register int i;
  794.  
  795.     if (nr < 1 || nr > line_count)
  796.     {
  797.         emsg("delsline: nr wrong");
  798.         return (alloc_line(0));
  799.     }
  800.     ptr = nr2ptr(nr);
  801.     adjustmark(ptr, NULL);            /* remove marks for this line */
  802.     bp = curr_block;
  803.     if (line_count == 1)    /* don't delete the last line in the file */
  804.     {
  805.         bp->b_ptr[0] = alloc_line(0);
  806.         return (ptr);
  807.     }
  808.     --line_count;
  809.  
  810.     /* move the rest of the ptrs in this block one down */
  811.     pp = &(bp->b_ptr[nr - curr_count]);
  812.     fp = &(bp->b_flags[nr - curr_count]);
  813.     for (i = bp->b_count + curr_count - nr - 1; --i >= 0; ++pp, ++fp)
  814.     {
  815.         *pp = *(pp + 1);
  816.         *fp = *(fp + 1);
  817.     }
  818.     if (--bp->b_count == 0) /* the block became empty, remove it from the list */
  819.     {
  820.         if (bp->b_prev == NULL)
  821.             first_block = bp->b_next;
  822.         else
  823.             bp->b_prev->b_next = bp->b_next;
  824.         if (bp->b_next == NULL)
  825.             last_block = bp->b_prev;
  826.         else
  827.             bp->b_next->b_prev = bp->b_prev;
  828.         free_line((char *)bp);
  829.         curr_count = 0; /* curr_block invalid */
  830.     }
  831.     return (ptr);
  832. }
  833.  
  834. /*
  835.  * replace the line "lnum" with the line "new".
  836.  * return the old line (which should be freed by the caller)
  837.  */
  838.     char *
  839. replaceline(lnum, new)
  840.     linenr_t lnum;
  841.     char *new;
  842. {
  843.     char *old;
  844.  
  845.     old = nr2ptr(lnum);
  846.     if (new == NULL || curr_count == 0)    /* we don't want NULL pointers in the list */
  847.         return (alloc_line(0)); /* be friendly to the caller */
  848.  
  849.     curr_block->b_ptr[lnum - curr_count] = new;
  850.     curr_block->b_flags[lnum - curr_count] = 0;
  851.     adjustmark(old, new);
  852.     return (old);
  853. }
  854.  
  855. /*
  856.  * canincrease(n) - returns TRUE if the current line can be increased 'n'
  857.  * bytes
  858.  *
  859.  * This routine returns immediately if the requested space is available. If not,
  860.  * it attempts to allocate the space and adjust the data structures
  861.  * accordingly. If everything fails it returns FALSE.
  862.  */
  863.     int
  864. canincrease(n)
  865.     int    n;
  866. {
  867.     register char    *old;
  868.     register char    *new;        /* pointer to new space */
  869.     register unsigned newsize;
  870.  
  871.     old = nr2ptr(Curpos.lnum);
  872.     newsize = strlen(old) + n;
  873.  
  874.     new = alloc_line(newsize);
  875.     if (new == NULL)
  876.         return FALSE;
  877.  
  878.     strcpy(new, old);
  879.     adjustmark(old, new);
  880.     free_line(old);
  881.     curr_block->b_ptr[Curpos.lnum - curr_count] = new;
  882.     curr_block->b_flags[Curpos.lnum - curr_count] = 0;
  883.  
  884.     return TRUE;
  885. }
  886.